
#include "utils/util.h"
#include <system_error>

namespace Sivin {
  std::string SnUtil::readShaderFromPath(std::string path) {
    std::string result;
    std::ifstream file{path};
    std::string line;
    if (file.is_open()) {
      while (std::getline(file, line)) {
        result += line + "\n";
      }
      file.close();
    } else {
      std::error_code errCode(errno, std::system_category());
      std::cout << "Failed to open file:" << errCode.message() << std::endl;
    }
    return result;
  }

  /**
   * 注意：着色器代码如果为空，可以编译通过
   * 如果顶点着色器为空，则链接失败，如果片元着色器为空，也能链接成功，但没有输出
   * 如果着色器语法错误，则编译失败
   */
  GLuint SnUtil::createShaderProgram(const char *vertShaderStr, const char *fragShaderStr) {
    GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER);
    glShaderSource(vertexShader, 1, &vertShaderStr, nullptr);
    glCompileShader(vertexShader);
    if (checkComplieStatus(vertexShader) != GL_TRUE) {
      std::cout << "compile vertex shader faild" << std::endl;
      return 0;
    }

    GLuint fragShader = glCreateShader(GL_FRAGMENT_SHADER);
    glShaderSource(fragShader, 1, &fragShaderStr, nullptr);
    glCompileShader(fragShader);
    if (checkComplieStatus(fragShader) != GL_TRUE) {
      std::cout << "compile fragment shader faild" << std::endl;
      return 0;
    }

    GLuint shaderProgram = glCreateProgram();
    glAttachShader(shaderProgram, vertexShader);
    glAttachShader(shaderProgram, fragShader);
    glLinkProgram(shaderProgram);

    if (checkLinkStatus(shaderProgram) != GL_TRUE) {
      std::cout << "link shader progream failed" << std::endl;
      return 0;
    }
    //链接完成，删除shader
    glDeleteShader(vertexShader);
    glDeleteShader(fragShader);
    return shaderProgram;
  }


  GLint SnUtil::checkComplieStatus(GLuint shader) {
    GLint compileStatus;
    char infoLog[512];
    glGetShaderiv(shader, GL_COMPILE_STATUS, &compileStatus);
    if (compileStatus != GL_TRUE) {
      glGetShaderInfoLog(shader, 512, NULL, infoLog);
      std::cout << "Info:" << infoLog << std::endl;
    }
    return compileStatus;
  }

  GLint SnUtil::checkLinkStatus(GLuint shader) {
    GLint linkStatus;
    char infoLog[512];
    glGetProgramiv(shader, GL_LINK_STATUS, &linkStatus);
    if (linkStatus != GL_TRUE) {
      glGetProgramInfoLog(shader, 512, NULL, infoLog);
      std::cout << "Info:" << infoLog << std::endl;
    }
    return linkStatus;
  }

  GLuint SnUtil::createTextureId() {
    GLuint textureId;
    glGenTextures(1, &textureId);
    glBindTexture(GL_TEXTURE_2D, textureId);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    return textureId;
  }

  std::string SnUtil::getGLError(GLenum error) {
    const char *errorString = "Unknown error";
    switch (error) {
      case GL_INVALID_ENUM:
        errorString = "GL_INVALID_ENUM";
        break;
      case GL_INVALID_VALUE:
        errorString = "GL_INVALID_VALUE";
        break;
      case GL_INVALID_OPERATION:
        errorString = "GL_INVALID_OPERATION";
        break;
      case GL_STACK_OVERFLOW:
        errorString = "GL_STACK_OVERFLOW";
        break;
      case GL_STACK_UNDERFLOW:
        errorString = "GL_STACK_UNDERFLOW";
        break;
      case GL_OUT_OF_MEMORY:
        errorString = "GL_OUT_OF_MEMORY";
        break;
        // add more cases as needed
    }
    return errorString;
  }
}// namespace Sivin